/*
 * @Description:
 * @Version: 1.0
 * @Autor: chen_yt
 * @Date: 2021-01-12 13:48:25
 * @LastEditors: wei_jt@ecidi.com
 * @LastEditTime: 2021-12-31 20:13:55
 */
import {
    registerMicroApps,
    start,   // 启动
    addGlobalUncaughtErrorHandler,
} from 'qiankun'
import { appInfos, formatAppInfo, subAppRoute } from './apps'
import store from '@/store'
import { mainApp } from '@/config'
import { getUserRoutes } from '@/utils/route'
import { loadLanguageAsync } from '@/utils/locale'
import Vue from 'vue'

var requestTime = 3000 //重连等待时间
var maxRequestTry = 5 //重连尝试次数
var __QIAN_KUN_CONTAINER__ = '#QKframe'//Qiankun渲染容器的id
var LOADING = null
var apps = []

/**
 * @description: 判断该path是否为子应用路由
 */
export function isRouteBelongSubApp (path) {
    let currentSubAppRoute = [...subAppRoute]
    if (apps.length > 0) {
        apps.forEach(item => {
            currentSubAppRoute.push(item.activeRoute)
        })
    }

    for (let i = 0; i < currentSubAppRoute.length; i++) {
        if (path.indexOf(currentSubAppRoute[i]) > -1) return true
    }
    return false
}

export function getAppList () {
    return apps
}

/**
 * @description: 额外增加一个子应用
 */
export function registerExtraApp (app) {
    //name检测
    //加入apps
    if (!mainApp) return false

    if (!app || !app.name) {
        console.error('子应用名称不能为空')
        return false
    }

    let props = {
        store,
        data: {},
        appName: app.name,
        appBasePath: app.basePath,
        func: {
            getUserRoutes
        },
    }
    let container = __QIAN_KUN_CONTAINER__

    app.container = app.container || container
    app.props = { ...app.props, ...props }

    let newApps = [app]
    formatAppInfo(newApps)

    if (apps.some(item =>
        item.name == newApps[0].name
    )) {
        console.error('同名子应用已存在')
        return false
    }

    apps = [...apps, ...newApps]

    registerMicroApp(newApps)

    return true
}

export function registerMicroApp (microApps) {
    //注册微服务
    registerMicroApps(
        microApps,
        {
            beforeLoad: [
                app => {
                    // console.log('beforeLoad')
                    showLoading()
                }
            ],
            beforeMount: [
                app => {
                    showLoading()
                    // console.log('beforeMount')
                }
            ],
            afterMount: [
                app => {
                    // console.log('afterMount')
                    hideLoading()
                }
            ],
            beforeUnmount: [
                app => {
                    // console.log('beforeUnmount')
                }
            ],
            afterUnmount: [
                app => {
                    // console.log('afterUnmount')
                }
            ],
        },
    )
}


/**
 * @description: 混入后导入默认的一些子应用
 */
export default {
    name: 'QianKun',
    computed: {
        //子应用状态下，观察主应用当前语言
        currentLang () {
            if (window.__POWERED_BY_QIANKUN__) {
                return this.$fksMainStore.state.currentLang
            }
            return ''
        },
        //子应用状态下，观察主应用当前门户
        currentPortal () {
            if (window.__POWERED_BY_QIANKUN__) {
                return this.$fksMainStore.state['portal']

            }
            return {}
        }
    },
    watch: {
        //以下为子应用才会使用的方法
        currentLang (val) {
            if (val)
                loadLanguageAsync(val)
        },
        currentPortal (val) {
            if (val.id) {
                //由于主应用切换门户后，会重新加载路由并回到distribute页面
                //故重新进入子应用时会自动读取application获得新路由
                //此处不用再处理
                // store.dispatch(CHANGE_PORTAL, portal)
                // store.dispatch(UPDATE_USER, portal.id)
                // this.$router.replace('/distribute')
            }
        },
        //以下为主应用才会使用的方法
        //是否进入了子应用界面
        routeBelongSubApp (val) {
            if (!mainApp) return
            if (!val) {
                hideLoading()
            }
        },
        $route (to, from) {
            if (!mainApp) return
            //与子应用切换无关时不watch
            if (!isRouteBelongSubApp(from.path) && !isRouteBelongSubApp(to.path)) {
                return
            }
            //切换出去时候停止所有重连尝试
            if (this.getBasePath(to.path) != this.getBasePath(from.path)) {
                hideLoading()
                for (let key in this.retryTimer) {
                    console.log('删除重连定时器')
                    //其实这边删不光，因为此时可能还有正在进行fetch的方法，只能在fetch里在控制一下
                    window.clearTimeout(this.retryTimer[key].timer)
                    this.retryTimer[key].timer = null
                    delete this.retryTimer[key]
                }
                //判断这次是否进入了一个连接错误的子应用
                let key = this.getBasePath(to.path)
                if (this.load404App[key]) {
                    console.log('进入了一个上次错误的子应用')
                    this.request(this.load404App[key])
                }
                //此次子应用进入404了，记录错误，下次再连
                if (to.path == '/404') {
                    //逻辑有问题
                    // let key = this.getBasePath(from.path)
                    // this.load404App[key] = 'http://127.0.0.1:8888'
                }
            }
        }
    },
    data () {
        return {
            that: this,
            mountedApp: {},
            load404App: {},
            retryTimer: {},
            isFirstMount: true,
            tryRequestNum: 0,
        }
    },
    mounted () {
        if (!mainApp) return
        if (this.isFirstMount) setTimeout(() => { this.initQiankun() }, 1000)
    },
    created () {
        hideLoading()
    },
    beforeDestroy () {
        hideLoading()
    },
    methods: {
        initQiankun () {
            this.isFirstMount = false

            apps = []

            //先导入配置好的子应用
            for (let i = 0; i < appInfos.length; i++) {
                let app = appInfos[i]
                let props = {
                    store,
                    data: {},
                    appName: app.name,
                    appBasePath: '',
                    func: {
                        getUserRoutes
                    },
                }
                apps.push({
                    ...app,
                    container: __QIAN_KUN_CONTAINER__,
                    props
                })
            }

            //添加异常处理
            addGlobalUncaughtErrorHandler(this.globalError)

            //真正注册方法
            registerMicroApp(apps)

            // 启动微服务
            start({
                fetch: this.request,                // 自定义fetch方法
                prefetch: false, //开启预加载
                sandbox: true,
                // sandbox: { strictStyleIsolation: true } //Qiankun2.0 沙箱
            })
        },



        getBasePath (url) {
            return url.split('/')[1]
        },




        globalError (e) {
            console.log('子应用加载出错:', e)
        },

        /**
         * @description: 子应用首个fetch时，显示loading
         */
        urlIsEntry (url) {
            for (let i = 0; i < apps.length; i++) {
                if (apps[i].entry === url)
                    return true
            }
            return false
        },

        // 自定义的 fetch钩子函数 用于连接子应用
        // 添加当前路径的基路径，用于重连判断
        request (url) {
            console.log('url:' + url)
            if (this.urlIsEntry(url)) {
                showLoading()
                this.tryRequestNum = 0
            }
            return this.newRequest(url, this.getBasePath(this.$route.path))
        },


        newRequest (url, mountedBaseRoute) {
            //该计时器触发时已不在原子应用中，停止fetch
            if (mountedBaseRoute !== this.getBasePath(this.$route.path)) {
                return
            }

            let that = this
            let key = this.getBasePath(this.$route.path)


            return new Promise((resolve, reject) => {
                fetch(url, {
                    //添加跨域
                    headers: {
                        'Access-Control-Request-Method': '*'
                    },
                    referrerPolicy: 'origin-when-cross-origin'
                })
                    .then(res => {
                        // 子应用未部署情况
                        if (res.status == 404) {
                            hideLoading()
                            that.$message.error('填写正确的项目地址或者请部署子应用')
                            // 将其url添加404url中 用于下次再点击提示
                            that.load404App[key] = url
                            that.$router.push('/403')
                            // 返回 状态给qiankun
                            resolve()
                        }
                        // 重连机制 如果成功 需清除 重连列表
                        that.tryRequestNum = 0
                        if (that.retryTimer[key]) {
                            window.clearTimeout(that.retryTimer[key].timer)
                            delete that.retryTimer[key]
                        }
                        if (that.load404App[key]) {
                            delete that.load404App[key]
                        }
                        // 返回子应用资源给 主应用 否则会一直 pending中...
                        resolve(res)
                    })
                    // 捕获 子应用加载错误
                    .catch(err => {
                        // 加载失败，添加入失败列表
                        that.load404App[key] = url
                        // 重新加载
                        // 设定超时时间  3s  3s内如果加载不出来 则进行再次加载
                        if (that.tryRequestNum < maxRequestTry) {
                            let timer = setTimeout(() => {
                                that.tryRequestNum++
                                if (LOADING) {
                                    LOADING.setText(`正在重连应用第${that.tryRequestNum}次`)
                                }
                                that.newRequest(url, mountedBaseRoute)
                            }, requestTime)
                            that.retryTimer[key] = { timer, url }
                        } else {
                            //重定向到subAppError页面,还没写
                            that.$router.push('/403')
                            that.$message.error('连接失败')
                            hideLoading()
                            that.tryRequestNum = 0
                        }
                        // 返回加载失败 去除pending状态
                        reject(err)
                    })
            })
        },


    }
}

function showLoading () {
    if (LOADING) {
        return
    }
    LOADING = Vue.prototype.$loading({
        lock: false,
        target: document.getElementsByClassName('fks-app-main')[0],
        spinner: 'fks-icon-loading',
        text: '正在加载中',
        color: '#fff',
        background: '#F1F2F6',
        customClass: 'app_load_loading'
    })
}


function hideLoading () {
    if (LOADING) {
        LOADING.close()
        LOADING = null
    }
}
